home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / AMICUS / AMICUS05.ADF / IFF / iffr.c < prev    next >
C/C++ Source or Header  |  1986-04-20  |  11KB  |  324 lines

  1.  
  2. /*----------------------------------------------------------------------*
  3.  * IFFR.C  Support routines for reading IFF-85 files.          1/23/86
  4.  * (IFF is Interchange Format File.)
  5.  *
  6.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  7.  * This software is in the public domain.
  8.  *
  9.  * This version for the Commodore-Amiga computer.
  10.  *
  11.  * Uses "gio".  Either link with gio.c, or set the GIO_ACTIVE flag to 0
  12.  * in gio.h.
  13.  *----------------------------------------------------------------------*/
  14. #include "iff/gio.h"
  15. #include "iff/iff.h"
  16.  
  17. /* ----- Private subroutine FileLength() --------------------------------*/
  18. /* Returns the length of the file or else a negative IFFP error code
  19.  * (NO_FILE or DOS_ERROR). AmigaDOS-specific implementation.
  20.  * SIDE EFFECT: Thanks to AmigaDOS, we have to change the file's position
  21.  * to find its length.
  22.  * Now if Amiga DOS maintained fh_End, we'd just do this:
  23.  *    fileLength = (FileHandle *)BADDR(file)->fh_End; */
  24. LONG FileLength(file)  BPTR file;  {
  25.     LONG fileLength = NO_FILE;
  26.  
  27.     if (file > 0)  {
  28.      GSeek(file, 0, OFFSET_END);   /* Seek to end of file.*/
  29.      fileLength = GSeek(file, 0, OFFSET_CURRENT);
  30.          /* Returns position BEFORE the seek, which is #bytes in file. */
  31.      if (fileLength < 0)
  32.          fileLength = DOS_ERROR;   /* DOS being absurd.*/
  33.      }
  34.  
  35.     return(fileLength);
  36.     }
  37.  
  38. /* ---------- Read -----------------------------------------------------*/
  39.  
  40. /* ---------- OpenRIFF --------------------------------------------------*/
  41. IFFP OpenRIFF(file0, new0, clientFrame)
  42.      BPTR file0;   GroupContext *new0;  ClientFrame *clientFrame; {
  43.     register BPTR file = file0;
  44.     register GroupContext *new = new0;
  45.     IFFP iffp = IFF_OKAY;
  46.  
  47.     new->parent       = NULL;      /* "whole file" has no parent.*/
  48.     new->clientFrame  = clientFrame;
  49.     new->file         = file;
  50.     new->position     = 0;
  51.     new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
  52.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  53.  
  54.     /* Set new->bound and go to the file's beginning. */
  55.     new->bound = FileLength(file);
  56.     if (new->bound < 0)
  57.      iffp = new->bound;          /* File system error! */
  58.     else if ( new->bound < sizeof(ChunkHeader) )
  59.      iffp = NOT_IFF;                  /* Too small for an IFF file. */
  60.     else
  61.      GSeek(file, 0, OFFSET_BEGINNING);  /* Go to file start. */
  62.  
  63.     return(iffp);
  64.     }
  65.  
  66. /* ---------- OpenRGroup -----------------------------------------------*/
  67. IFFP OpenRGroup(parent0, new0)   GroupContext *parent0, *new0; {
  68.     register GroupContext *parent = parent0;
  69.     register GroupContext *new    = new0;
  70.     IFFP iffp = IFF_OKAY;
  71.  
  72.     new->parent       = parent;
  73.     new->clientFrame  = parent->clientFrame;
  74.     new->file         = parent->file;
  75.     new->position     = parent->position;
  76.     new->bound        = parent->position + ChunkMoreBytes(parent);
  77.     new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
  78.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  79.  
  80.     if ( new->bound > parent->bound  ||  IS_ODD(new->bound) )
  81.      iffp = BAD_IFF;
  82.     return(iffp);
  83.     }
  84.  
  85. /* ---------- CloseRGroup -----------------------------------------------*/
  86. IFFP CloseRGroup(context)   GroupContext *context; {
  87.     register LONG position;
  88.  
  89.     if (context->parent == NULL) {
  90.      }  /* Context for whole file.*/
  91.     else {
  92.      position = context->position;
  93.      context->parent->bytesSoFar += position - context->parent->position;
  94.      context->parent->position = position;
  95.      }
  96.     return(IFF_OKAY);
  97.     }
  98.  
  99. /* ---------- SkipFwd --------------------------------------------------*/
  100. /* Skip over bytes in a context. Won't go backwards.*/
  101. /* Updates context->position but not context->bytesSoFar.*/
  102. /* This implementation is AmigaDOS specific.*/
  103. IFFP SkipFwd(context, bytes)   GroupContext *context;  LONG bytes; {
  104.     IFFP iffp = IFF_OKAY;
  105.  
  106.     if (bytes > 0) {
  107.      if (-1 == GSeek(context->file, bytes, OFFSET_CURRENT))
  108.          iffp = BAD_IFF; /* Ran out of bytes before chunk complete.*/
  109.      else
  110.          context->position += bytes;
  111.      }
  112.     return(iffp);
  113.     }
  114.  
  115. /* ---------- GetChunkHdr ----------------------------------------------*/
  116. ID GetChunkHdr(context0)   GroupContext *context0;  {
  117.     register GroupContext *context = context0;
  118.     register IFFP iffp;
  119.     LONG remaining;
  120.  
  121.     /* Skip remainder of previous chunk & padding. */
  122.     iffp = SkipFwd(context,
  123.      ChunkMoreBytes(context) + IS_ODD(context->ckHdr.ckSize));
  124.     CheckIFFP();
  125.  
  126.     /* Set up to read the new header. */
  127.     context->ckHdr.ckID = BAD_IFF; /* Until we know it's okay, mark it BAD.*/
  128.     context->subtype    = NULL_CHUNK;
  129.     context->bytesSoFar = 0;
  130.  
  131.     /* Generate a psuedo-chunk if at end-of-context. */
  132.     remaining = context->bound - context->position;
  133.     if (remaining == 0) {
  134.      context->ckHdr.ckSize = 0;
  135.      context->ckHdr.ckID   = END_MARK;
  136.      }
  137.  
  138.     /* BAD_IFF if not enough bytes in the context for a ChunkHeader.*/
  139.     else if (sizeof(ChunkHeader) > remaining) {
  140.      context->ckHdr.ckSize = remaining;
  141.      }
  142.  
  143.     /* Read the chunk header (finally). */
  144.     else {
  145.         switch (
  146.          GRead(context->file, (BYTE *)&context->ckHdr, sizeof(ChunkHeader))
  147.          ) {
  148.          case -1: return(context->ckHdr.ckID = DOS_ERROR);
  149.          case 0:  return(context->ckHdr.ckID = BAD_IFF);
  150.          }
  151.  
  152.      /* Check: Top level chunk must be LIST or FORM or CAT. */
  153.      if (context->parent == NULL)
  154.          switch(context->ckHdr.ckID) {
  155.           case FORM:  case LIST:  case CAT:  break;
  156.           default:    return(context->ckHdr.ckID = NOT_IFF);
  157.           }
  158.  
  159.      /* Update the context. */
  160.      context->position += sizeof(ChunkHeader);
  161.      remaining         -= sizeof(ChunkHeader);
  162.  
  163.      /* Non-positive ID values are illegal and used for error codes.*/
  164.      /* We could check for other illegal IDs...*/
  165.      if (context->ckHdr.ckID <= 0)
  166.           context->ckHdr.ckID = BAD_IFF;
  167.  
  168.      /* Check: ckSize negative or larger than # bytes left in context? */
  169.      else if (context->ckHdr.ckSize < 0  ||
  170.            context->ckHdr.ckSize > remaining) {
  171.          context->ckHdr.ckSize = remaining;
  172.          context->ckHdr.ckID   = BAD_IFF;
  173.          }
  174.  
  175.      /* Automatically read the LIST, FORM, PROP, or CAT subtype ID */
  176.      else switch (context->ckHdr.ckID) {
  177.          case LIST:  case FORM:  case PROP:  case CAT:  {
  178.           iffp = IFFReadBytes(context,
  179.                         (BYTE *)&context->subtype,
  180.                         sizeof(ID));
  181.           if (iffp != IFF_OKAY)
  182.               context->ckHdr.ckID = iffp;
  183.           break; }
  184.          }
  185.  
  186.      }
  187.     return(context->ckHdr.ckID);
  188.     }
  189.  
  190. /* ---------- IFFReadBytes ---------------------------------------------*/
  191. IFFP IFFReadBytes(context, buffer, nBytes)
  192.     GroupContext *context;   BYTE *buffer;   LONG nBytes; {
  193.     register IFFP iffp = IFF_OKAY;
  194.  
  195.     if (nBytes < 0)
  196.      iffp = CLIENT_ERROR;
  197.     else if (nBytes > ChunkMoreBytes(context))
  198.      iffp = SHORT_CHUNK;
  199.     else if (nBytes > 0)
  200.      switch ( GRead(context->file, buffer, nBytes) ) {
  201.          case -1: {iffp = DOS_ERROR; break; }
  202.          case 0:  {iffp = BAD_IFF;   break; }
  203.          default: {
  204.           context->position   += nBytes;
  205.           context->bytesSoFar += nBytes;
  206.           }
  207.          }
  208.  
  209.     return(iffp);
  210.     }
  211.  
  212. /* ---------- SkipGroup ------------------------------------------------*/
  213. IFFP SkipGroup(context)  GroupContext *context;  {
  214.     }     /* Nothing to do, thanks to GetChunkHdr */
  215.  
  216. /* ---------- ReadIFF --------------------------------------------------*/
  217. IFFP ReadIFF(file, clientFrame)  BPTR file;  ClientFrame *clientFrame;  {
  218.     /*CompilerBug register*/ IFFP iffp;
  219.     GroupContext context;
  220.  
  221.     iffp = OpenRIFF(file, &context);
  222.     context.clientFrame = clientFrame;
  223.  
  224.     if (iffp == IFF_OKAY)
  225.      switch (iffp = GetChunkHdr(&context)) {
  226.          case FORM: { iffp = (*clientFrame->getForm)(&context); break; }
  227.          case LIST: { iffp = (*clientFrame->getList)(&context); break; }
  228.          case CAT : { iffp = (*clientFrame->getCat )(&context); break; }
  229.          /* default: Includes IFF_DONE, BAD_IFF, NOT_IFF... */
  230.          }
  231.  
  232.     CloseRGroup(&context);
  233.  
  234.     if (iffp > 0)        /* Make sure we don't return an ID.*/
  235.      iffp = NOT_IFF;          /* GetChunkHdr should've caught this.*/
  236.     return(iffp);
  237.     }
  238.  
  239. /* ---------- ReadIList ------------------------------------------------*/
  240. IFFP ReadIList(parent, clientFrame)
  241.     GroupContext *parent;  ClientFrame *clientFrame; {
  242.     GroupContext listContext;
  243.     IFFP iffp;
  244.     BOOL propOk = TRUE;
  245.  
  246.     iffp = OpenRGroup(parent, &listContext);
  247.     CheckIFFP();
  248.  
  249.     /* One special case test lets us handle CATs as well as LISTs.*/
  250.     if (parent->ckHdr.ckID == CAT)
  251.      propOk = FALSE;
  252.     else
  253.      listContext.clientFrame = clientFrame;
  254.  
  255.     do {
  256.      switch (iffp = GetChunkHdr(&listContext)) {
  257.          case PROP: {
  258.           if (propOk)
  259.               iffp = (*clientFrame->getProp)(&listContext);
  260.           else
  261.               iffp = BAD_IFF;
  262.           break;
  263.           }
  264.          case FORM: { iffp = (*clientFrame->getForm)(&listContext); break; }
  265.          case LIST: { iffp = (*clientFrame->getList)(&listContext); break; }
  266.          case CAT : { iffp = (*clientFrame->getCat )(&listContext); break; }
  267.          /* default: Includes END_MARK, IFF_DONE, BAD_IFF, NOT_IFF... */
  268.          }
  269.      if (listContext.ckHdr.ckID != PROP)
  270.          propOk = FALSE; /* No PROPs allowed after this point.*/
  271.      } while (iffp == IFF_OKAY);
  272.  
  273.     CloseRGroup(&listContext);
  274.  
  275.     if (iffp > 0)   /* Only chunk types above are allowed in a LIST/CAT.*/
  276.      iffp = BAD_IFF;
  277.     return(iffp == END_MARK ? IFF_OKAY : iffp);
  278.     }
  279.  
  280. /* ---------- ReadICat -------------------------------------------------*/
  281. /* By special arrangement with the ReadIList implement'n, this is trivial.*/
  282. IFFP ReadICat(parent)  GroupContext *parent;  {
  283.     return( ReadIList(parent, NULL) );
  284.     }
  285.  
  286. /* ---------- GetFChunkHdr ---------------------------------------------*/
  287. ID GetFChunkHdr(context)   GroupContext *context; {
  288.     register ID id;
  289.  
  290.     id = GetChunkHdr(context);
  291.     if (id == PROP)
  292.      context->ckHdr.ckID = id = BAD_IFF;
  293.     return(id);
  294.     }
  295.  
  296. /* ---------- GetF1ChunkHdr ---------------------------------------------*/
  297. ID GetF1ChunkHdr(context)   GroupContext *context; {
  298.     register ID id;
  299.     register ClientFrame *clientFrame = context->clientFrame;
  300.  
  301.     switch (id = GetChunkHdr(context))  {
  302.      case PROP: { id = BAD_IFF; break; }
  303.      case FORM: { id = (*clientFrame->getForm)(context); break; }
  304.      case LIST: { id = (*clientFrame->getList)(context); break; }
  305.      case CAT : { id = (*clientFrame->getCat )(context); break; }
  306.      /* Default: let the caller handle other chunks */
  307.      }
  308.     return(context->ckHdr.ckID = id);
  309.     }
  310.  
  311. /* ---------- GetPChunkHdr ---------------------------------------------*/
  312. ID GetPChunkHdr(context)   GroupContext *context; {
  313.     register ID id;
  314.  
  315.     id = GetChunkHdr(context);
  316.     switch (id) {
  317.      case LIST:  case FORM:  case PROP:  case CAT:  {
  318.          id = context->ckHdr.ckID = BAD_IFF;
  319.          break; }
  320.      }
  321.     return(id);
  322.     }
  323.  
  324.